﻿import os
import sys
import random
import pygame
from pygame.locals import *
from pygame_dep import *
from ezudp import *

COVER_OPEN = 1
COVER_OPEN_FOR_ME = 2
COVER_OPEN_FOR_HIM = 3
COVER_CLOSE = 4

EFFECT_BORDER_LIGHT = 2
EFFECT_TOP_DARK = 4
EFFECT_ALL_DARK = 8
EFFECT_ALL_LIGHT = 16
'''
def get_key():
	while 1:
		event = pygame.event.poll()
		if event.type == KEYDOWN:
			return event.key
		else:
			pass

def display_box(screen, message):
	"Print a message in a box in the middle of the screen"
	fontobject = pygame.font.Font(None,18)
	pygame.draw.rect(screen, (0,0,0),
					(	(screen.get_width() / 2) - 100,
						(screen.get_height() / 2) - 10,
						200,20), 0)
						
	pygame.draw.rect(screen, (255,255,255),
					((screen.get_width() / 2) - 102,
					(screen.get_height() / 2) - 12,
					204,24), 1)
					
	if len(message) != 0:
		screen.blit(fontobject.render(message, 1, (255,255,255)),
		((screen.get_width() / 2) - 100, (screen.get_height() / 2) - 10))
	pygame.display.flip()

def ask(screen, question):
	"ask(screen, question) -> answer"
	pygame.font.init()
	current_string = []
	display_box(screen, question + ": ".join(current_string))
	while 1:
		inkey = get_key()
		if inkey == K_BACKSPACE:
			current_string = current_string[0:-1]
		elif inkey == K_RETURN:
			break
		elif inkey == K_MINUS:
			current_string.append("_")
		elif inkey <= 127:
			current_string.append(chr(inkey))
		display_box(screen, question + ": ".join(current_string))
	return "".join(current_string)
'''

class Card():
	idcount = 0
	def __init__(self, name, id=None, x=0, y=0, turn180=False):
		if id == None:
			Card.idcount += 1
			self.id = Card.idcount
		else:
			self.id = id
			
		self.name = name
		self.cover = COVER_CLOSE
		self.line = 0
		
		self.x = x
		self.y = y
		self.width = 120
		self.height = 170
		
		self.turn180 = turn180
		
	def collide(self, pointx, pointy):
		iscollide = False
		if self.x - self.width/2 < pointx and  pointx < self.x + self.width/2:
			if self.y - self.height/2 < pointy and  pointy < self.y + self.height/2:
				iscollide = True
		
		return iscollide
	
	# 取得參數點與卡片中心點的距離
	#def clength(self, pointx, pointy):
	#	self.x + self.width/2
	

def serverloop(screen, udpsocket, targetaddr, deck, exdeck=None):
	timeticker = pygame.time.Clock()
	sendqueue = []
	cardlist = []
	
	cardname_map_cardno = {}
	for cardnumber, cardname in deck:
		cardname_map_cardno[cardname] = cardnumber
		sendqueue.append("map:%s,%s" % (cardname, cardnumber))
		
	random.shuffle(deck)
		
	for cardnumber, cardname in deck:
		cardlist.append(Card(cardname, None, 900, 600))
		sendqueue.append("create:%s,%d,%d,%d" %
			(cardlist[-1].name, cardlist[-1].id, cardlist[-1].x, cardlist[-1].y))
		
	
	tool = "Hand"
	
	hangs = []
	aparts = []
	apart_origin = (0, 0)
	hang_center = (0, 0)
	
	while(1):
		timeticker.tick(60)
		screen.screen.fill((0,0,0))
		
		keys = pygame.key.get_pressed()
		
		
		if keys[pygame.K_LSHIFT]:
			toolall = True
		else:
			toolall = False
			
		if keys[pygame.K_c]:
			tool = "Cover"
		elif keys[pygame.K_s]:
			tool = "Show"
		elif keys[pygame.K_g]:
			tool = "FaceGround"
		elif keys[pygame.K_r]:
			tool = "Random"
			toolall = True
		elif keys[pygame.K_z]:
			tool = "Line"
		elif keys[pygame.K_a]:
			tool = "Apart"
			toolall = True
		elif keys[pygame.K_m]:
			tool = "Magnet"
		else:
			tool = "Hand"
			
		for event in pygame.event.get((pygame.MOUSEBUTTONUP, pygame.MOUSEMOTION)):
			if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
				if not hangs and not aparts:
					for card in cardlist:
						if card.collide(event.pos[0], event.pos[1]):
							if tool == "Hand" or tool == "Magnet":
								hang_center = (event.pos[0] - card.x, event.pos[1] - card.y)
								hangs.append(card)
							elif tool == "Apart":
								apart_origin = (card.x, card.y)
								aparts.append(card)
							elif tool == "Cover":
								card.cover = COVER_OPEN_FOR_ME
								sendqueue.append("cover:%d" % (card.id))
							elif tool == "Show":
								card.cover = COVER_OPEN
								sendqueue.append("open:%d" % (card.id))
							elif tool == "FaceGround":
								card.cover = COVER_CLOSE
								sendqueue.append("close:%d" % (card.id))
							elif tool == "Line":
								if card.line == 1:
									card.line = 0
								else:
									card.line = 1
								sendqueue.append("line:%d,%d" % (card.id, card.line))
								
								
							if toolall == False:
								break
								
					if len(aparts) == 1:
						hangs.append(aparts[0])
						aparts = []
						
					if tool == "Random":
						iddata = ""
						shufflecards = []
						for card in cardlist:
							if card.collide(event.pos[0], event.pos[1]):
								shufflecards.append(card)
								iddata += str(card.id) + ","
								
						random.shuffle(shufflecards)
						for scard in shufflecards:
							cardlist.remove(scard)
							cardlist.append(scard)
						
						sendqueue.append("shuffle:%s" % (iddata[:-1]))
					
				elif hangs:
					#for hang in hangs:
					#	sendqueue.append("move:%d,%d,%d" % (hang.id, hang.x, hang.y))
					hangs = []
					
				elif aparts:
					#for apart in aparts:
					#	sendqueue.append("move:%d,%d,%d" % (apart.id, apart.x, apart.y))
					aparts = []
							
			if event.type == pygame.MOUSEMOTION:
				if tool == "Magnet":
					for card in cardlist:
						if card.collide(event.pos[0], event.pos[1]):
							hangs.append(card)
				if hangs:
					for hang in hangs:
						hang.x = event.pos[0] - hang_center[0]
						hang.y = event.pos[1] - hang_center[1]
						sendqueue.append("move:%d,%d,%d" % (hang.id, hang.x, hang.y))
				
				if aparts:
					for i in range(len(aparts)):
						rate = float(i) / len(aparts)
						newx = apart_origin[0] + (event.pos[0] - apart_origin[0]) * rate
						newy = apart_origin[1] + (event.pos[1] - apart_origin[1]) * rate
						aparts[i].x = newx
						aparts[i].y = newy
						sendqueue.append("move:%d,%d,%d" % (aparts[i].id, aparts[i].x, aparts[i].y))
						
				

							
		pygame.event.clear()
			
		mousepos = pygame.mouse.get_pos()
		lights = []
		if not tool == "Magnet" and not hangs and not aparts:
			for card in cardlist:
				if card.collide(mousepos[0], mousepos[1]):
					lights.append(card)
		lights.extend(hangs)
		
		# 覆蓋優化 - 針對完全重疊在一起的卡片，只顯示最上方第一張
		recardlist = cardlist[:]
		remove_list = []
		duplicate_points = []
		for card in recardlist:
			if (card.x, card.y, card.line) not in duplicate_points:
				duplicate_points.append((card.x, card.y, card.line))
			else:
				remove_list.append(card)
			
		for remove_card in remove_list:
			recardlist.remove(remove_card)
		del remove_list
		
		
		# 繪製卡片
		recardlist.reverse()
		for card in recardlist:
			angle =  card.line * 90 + int(card.turn180) * 180
			effect = 0
			if card.cover == COVER_OPEN:
				cardnumber = cardname_map_cardno[card.name]
				effect |= 0
			elif card.cover == COVER_OPEN_FOR_ME:
				cardnumber = cardname_map_cardno[card.name]
				effect |= EFFECT_TOP_DARK
			elif card.cover == COVER_OPEN_FOR_HIM:
				cardnumber = "__back"
				effect = EFFECT_ALL_LIGHT
			elif card.cover == COVER_CLOSE:
				cardnumber = "__back"
				effect |= 0
			if card in lights:
				effect |= EFFECT_BORDER_LIGHT
				
			
				
			cardsurface = fetchcard(cardnumber, effect, angle)
			screen.screen.blit(cardsurface, (card.x - card.width/2, card.y - card.height/2))
			
			
		# 繪製鼠標圖示
		if toolall == True:
			iconsurface = fetch("mouse_icon_plus_out.bmp")
		else:
			iconsurface = fetch("mouse_icon_out.bmp")
			
		if tool == "Hand":
			pygame.mouse.set_visible(True)
		else:
			pygame.mouse.set_visible(False)
			
		
		if tool == "Cover":
			screen.screen.blit(iconsurface, mousepos, (64*5,0,64,64))
		elif tool == "Show":
			screen.screen.blit(iconsurface, mousepos, (64*3,0,64,64))
		elif tool == "FaceGround":
			screen.screen.blit(iconsurface, mousepos, (64*0,0,64,64))
		elif tool == "Random":
			screen.screen.blit(iconsurface, mousepos, (64*2,0,64,64))
		elif tool == "Line":
			screen.screen.blit(iconsurface, mousepos, (64*1,0,64,64))
		elif tool == "Apart":
			screen.screen.blit(iconsurface, mousepos, (64*4,0,64,64))
		elif tool == "Magnet":
			screen.screen.blit(iconsurface, mousepos, (64*6,0,64,64))
		screen.flip()
		
		
		receivequeue = netexchange(udpsocket, targetaddr, sendqueue)
		sendqueue = []
		
		for msg in receivequeue:
			cmd = msg.split(":")[0]
			datas = msg.split(":")[1].split(",")
			if cmd == "create":
				x = screen.width - int(datas[2])
				y = screen.height - int(datas[3])
				cardlist.append(Card(datas[0], int(datas[1]), x, y, True))
			elif cmd == "move":
				for card in cardlist:
					if card.id == int(datas[0]):
						x = screen.width - int(datas[1])
						y = screen.height - int(datas[2])
						card.x = x
						card.y = y
						break
			elif cmd == "open":
				for card in cardlist:
					if card.id == int(datas[0]):
						card.cover = COVER_OPEN
						break
			elif cmd == "cover":
				for card in cardlist:
					if card.id == int(datas[0]):
						card.cover = COVER_OPEN_FOR_HIM
						break
			elif cmd == "close":
				for card in cardlist:
					if card.id == int(datas[0]):
						card.cover = COVER_CLOSE
						break
			elif cmd == "line":
				print("line")
				for card in cardlist:
					if card.id == int(datas[0]):
						card.line = int(datas[1])
						break
						
			elif cmd == "shuffle":
				shufflecards = []
				for id in datas:
					for card in cardlist:
						if card.id == id:
							shufflecards.append(card)
				
				for scard in shufflecards:
					cardlist.remove(scard)
					cardlist.append(scard)
			elif cmd == "map":
				cardname_map_cardno[datas[0]] = datas[1]
				#print(picmap)

def display_button(screen, y, w, text):
	"Print a button in the middle of the screen"
	fontobject = pygame.font.Font(None,18)
	x = screen.get_width() / 2 - w / 2
	h = 20
	pygame.draw.rect(screen, (255,255,255), (x, y, w, h), 1)
	
	if len(text) != 0:
		screen.blit(fontobject.render(text, 1, (255,255,255)),
		(x + 10, y + 5))
		
	return (x, y, w, h)

						
def display_box(screen, message):
	"Print a message in a box in the middle of the screen"
	fontobject = pygame.font.Font(None,18)
	pygame.draw.rect(screen, (128,128,128),
					(	(screen.get_width() / 2) - 100,
						(screen.get_height() / 2) - 10,
						200,20), 0)
						
	pygame.draw.rect(screen, (255,255,255),
					((screen.get_width() / 2) - 102,
					(screen.get_height() / 2) - 12,
					204,24), 1)
					
	if len(message) != 0:
		screen.blit(fontobject.render(message, 1, (255,255,255)),
		((screen.get_width() / 2) - 100, (screen.get_height() / 2) - 10))

	
def menuloop(screen):
	timeticker = pygame.time.Clock()
	pygame.font.init()
	
	phase = "SeleteMode"
	question = b"IPAddress"
	current_string = []
	
	while(1):
		timeticker.tick(60)
		screen.screen.fill((0,0,0))
		
		mouse_click = None
		inkey = 1024
		for event in pygame.event.get():
			if event.type == pygame.KEYDOWN:
				inkey = event.key
			if event.type == pygame.QUIT:
				screen.close()
			if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
				mouse_click = event.pos
			
		if phase == "SeleteMode":
			b1 = display_button(screen.screen, 500, 120, "Create Server")
			b2 = display_button(screen.screen, 535, 120, "Connect")
			if mouse_click:
				if 	mouse_click[0] > b1[0] and mouse_click[0] < b1[0] + b1[2] and \
				mouse_click[1] > b1[1] and mouse_click[1] < b1[1] + b1[3]:
					return ("-s",)
				if 	mouse_click[0] > b2[0] and mouse_click[0] < b2[0] + b2[2] and \
				mouse_click[1] > b2[1] and mouse_click[1] < b2[1] + b2[3]:
					phase = "IPInput"
			
		elif phase == "IPInput":
			if inkey == K_BACKSPACE:
				current_string = current_string[0:-1]
			elif inkey == K_RETURN:
				return ("-c", (b"".join(current_string)).decode("cp950"))
			#elif inkey == K_MINUS:
			#	current_string.append(b"_")
			#elif inkey <= 127 :
			#	current_string.append(bytes(chr(inkey), "ascii"))
			elif inkey >= K_0 and inkey <= K_9:
				current_string.append(bytes(chr(inkey), "ascii"))
			elif inkey >= K_KP0 and inkey <= K_KP9:
				inkey = inkey - K_KP0 + K_0
				current_string.append(bytes(chr(inkey), "ascii"))
			elif inkey == K_PERIOD or inkey == K_KP_PERIOD:
				current_string.append(bytes(chr(K_PERIOD), "ascii"))
				
			#print(question + b": " + b"".join(current_string))
			display_box(screen.screen, question + b": " + b"".join(current_string))
		
		screen.flip()
		
class WaitCallobj():
	def __init__(self, screen):
		self.screen = screen
		self.timeticker = pygame.time.Clock()
		self.count = 0
		
	def wait_callback(self):
		self.timeticker.tick(60)
		self.screen.screen.fill((0,0,0))
		self.count += 1
		for event in pygame.event.get():
			if event.type == pygame.QUIT:
				self.screen.close()
			
		posx = self.screen.screen.get_width() / 2 - 250 / 2
		posy = self.screen.screen.get_height() / 2 - 250 / 2
		ani_num = int(self.count / 60) % 4 + 1
		self.screen.screen.blit(fetch("waitting%02d.bmp" % (ani_num)), (posx, posy))
		self.screen.flip()
		
cardimages = {}
program_path = ""
image_path = ""
def fetch(name):
	return barn.fetch(bytes(os.path.join(image_path, name), "cp950")) 
	
fetchcard_cache = {}
def fetchcard(cardnumber, effect=None, angle=0):

	# 先尋找要的卡片是否在快取之中，有則取之
	if (cardnumber, effect, angle) in fetchcard_cache:
		return fetchcard_cache[(cardnumber, effect, angle)]
	else:
		# 如果卡片編號是__back，則要求的是卡背圖案，進行特殊處理
		if cardnumber == "__back":
			surface = fetch("deck.jpg")
			if effect & EFFECT_ALL_LIGHT:
				surface = fetch("covercard.jpg")
		else:
			surface = fetch(cardimages[cardnumber])

			# 找不到卡片的圖案的話，則使用代替圖示
			if surface == None:
				surface = fetch("nocard.jpg")
				
			# 處理卡片的發光與變暗等特效
			if effect & EFFECT_TOP_DARK:
				surface = surface.copy()
				lightermask = fetch("darker.bmp")
				surface.blit(lightermask, (0,0), None, BLEND_SUB)
			if effect & EFFECT_ALL_LIGHT:
				surface = surface.copy()
				lightermask = fetch("lighter.bmp")
				surface.blit(lightermask, (0,0), None, BLEND_ADD)
		
		if effect & EFFECT_BORDER_LIGHT:
			surface = surface.copy()
			lightermask = fetch("lighter_border.bmp")
			#darkmask = fetch("cardmask.bmp")
			surface.blit(lightermask, (0,0), None, BLEND_ADD)
		
			
		# 處理卡片的旋轉
		if angle == 90:
			surface = pygame.transform.rotate(surface, 90)
		elif angle == 180:
			surface = pygame.transform.rotate(surface, 180)
		elif angle == 270:
			surface = pygame.transform.rotate(surface, 270)
		fetchcard_cache[(cardnumber, effect, angle)] = surface
		return surface

	

def main():
	global program_path, image_path, cardimages
	screen = PygameGraphSys()
	screen.open()
	pygame.display.set_caption("Connect Card - 1.08")
	
	program_path = os.path.dirname(sys.argv[0])
	image_path = os.path.join(program_path, "images")
	cardimage_locates = ["..\\cardpic", "cardpic"]
	deck_locates = ["deck"]
	
	cardimage_paths = []
	for locate in cardimage_locates:
		path = os.path.abspath(os.path.join(program_path, locate))
		if os.path.exists(path):
			cardimage_paths.append(path)
	
	deck_paths = []
	for locate in deck_locates:
		path = os.path.abspath(os.path.join(program_path, locate))
		if os.path.exists(path):
			deck_paths.append(path)
		
	print(image_path)
	print(cardimage_paths)
	print(deck_paths)
	
	request_images = [	"nocard.jpg", "deck.jpg", "covercard.jpg", "cardmask.bmp",
						"waitting01.bmp", "waitting02.bmp", "waitting03.bmp", "waitting04.bmp",
						"mouse_icon_out.bmp"
					]
	
	# 嘗試讀取遊戲需要的圖檔
	for rqimg in request_images:
		#print(os.path.join(image_path, rqimg))
		if barn.fetch(bytes(os.path.join(image_path, rqimg), "cp950")) == None:
			print("Fail To Load %s" % (rqimg))
		
		
	# 尋找所有可用的卡片圖，並且對應編號與圖檔路徑
	image_exts = [".jpg", ".bmp", ".gif", ".png"]
	finded_cardimages = {}
	for cardimage_path in cardimage_paths:
		for filename in os.listdir(cardimage_path):
			if filename[-4:] in image_exts:
				cardnumber = filename[:-4].lower()
				finded_cardimages[cardnumber] = os.path.join(cardimage_path, filename)
			dirpath = os.path.join(cardimage_path, filename)
			if os.path.isdir(dirpath):
				for filename2 in os.listdir(dirpath):
					cardnumber = filename2[:-4].lower()
					if filename2[-4:] in image_exts:
						finded_cardimages[cardnumber] = os.path.join(dirpath, filename2)
	cardimages = finded_cardimages
	
	# 尋找所有可用的牌組
	finded_deck_paths = []
	for deck_path in deck_paths:
		for filename in os.listdir(deck_path):
			if filename[-4:] == ".txt":
				finded_deck_paths.append(os.path.join(deck_path, filename))
		
	# 讀取牌組，確定卡片圖可用
	used_deck = []
	for finded_deck_path in finded_deck_paths:
		f = open(os.path.join(finded_deck_path), "r")
		deck = f.readlines()
		f.close()
		for cardstr in deck:
			if cardstr[-1] == '\n':
				cardstr = cardstr[:-1]
			cardstrsp = cardstr.split(" ")
			
			cardnumber = cardstrsp[0].lower()
			cardname = cardstrsp[1]
			used_deck.append((cardnumber, cardname))
			if cardnumber in finded_cardimages:
				pass
			else:
				print(cardname + " 找不到圖檔")
			
	
	if len(sys.argv) < 2:
		argv = list(menuloop(screen))
		argv.insert(0, "")
		print(argv)
	else:
		argv = sys.argv
		
	#cmd = sys.argv[1]
	#if cmd == "c":
	#	ip = sys.argv[2]
			
	wait_callobj = WaitCallobj(screen)
	if argv[1] == "-s":
		udpsocket, targetaddr = createudp_server(21500, wait_callobj.wait_callback)
				
		Card.idcount = 0
		serverloop(screen, udpsocket, targetaddr, used_deck)
		
	elif argv[1] == "-c":
		udpsocket, targetaddr = createudp_client(argv[2], 21500, wait_callobj.wait_callback)
			
		Card.idcount = 1000
		serverloop(screen, udpsocket, targetaddr, used_deck)

	
if __name__ == '__main__':
	main()